package com.Utils3D;

import java.io.IOException;
import java.io.InputStream;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.util.ArrayList;
import java.util.Vector;

import mri.v3ds.Exception3ds;
import mri.v3ds.Face3ds;
import mri.v3ds.Mesh3ds;
import mri.v3ds.Scene3ds;
import mri.v3ds.TexCoord3ds;
import mri.v3ds.Vertex3ds;
import android.content.res.AssetManager;

import com.EdificacioAR.DebugLog;

public class Loader3ds extends GenericModelLoader {

	public static Model3D load3dsModel(String filename) {
		
		DebugLog.LOGD("Loader3ds::load3dsModel - Opening 3ds file: " + filename);
		
		return load3dsModel(filename, null);
	}
	
	public static Model3D load3dsModel(String filename, AssetManager asset) {
		InputStream stream = null;
		
		try {
			DebugLog.LOGD("Loader3ds::load3dsModel - Opening 3ds file: " + filename);
			stream = asset.open(filename);
			
			Scene3ds scene = new Scene3ds(stream);
			Mesh3ds mesh = scene.mesh(0);
			Model3D m = new Model3D();
			
			DebugLog.LOGD("Loader3ds::load3dsModel - 3ds file read. start with the parsing");
			
			/*
			DebugLog.LOGD("Loader3ds::load3dsModel - num meshes: " + scene.meshes());
			DebugLog.LOGD("Loader3ds::load3dsModel - num faces: " + scene.mesh(0).faces());
			DebugLog.LOGD("Loader3ds::load3dsModel - num vertices: " + scene.mesh(0).vertices());
			DebugLog.LOGD("Loader3ds::load3dsModel - num tex coords: " + scene.mesh(0).texCoords());
			*/
			
			/*
			// Fill the vertices vector
			m.vertices = FloatBuffer.allocate(mesh.vertices() * 3 * FLOAT_SIZE_BYTES);
			for (int i = 0; i < mesh.vertices(); i++)
			{
				Vertex3ds v = mesh.vertexArray()[i];
				m.vertices.put(v.X);
				m.vertices.put(v.Y);
				m.vertices.put(v.Z);
			}
			
			// Fill the tex coords vector
			m.texCoords = FloatBuffer.allocate(mesh.texCoords() * 2 * FLOAT_SIZE_BYTES);
			for (int i = 0; i < mesh.texCoords(); i++)
			{
				TexCoord3ds tc = mesh.texCoordArray()[i];
				m.texCoords.put(tc.U);
				m.texCoords.put(tc.V);
			}*/
			
			// Fill the indices and compute the face normals vector
			m.groups = new ArrayList<MeshGroup>();
			MeshGroup group = new MeshGroup();
			
			m.faceNormals = new Vector<Vector3>(mesh.faces());
			group.indices = IntBuffer.allocate(mesh.faces() * INTEGER_SIZE_BYTES);
			group.texFile = "";
			
			for (int i = 0; i < mesh.faces(); i++)
			{
				Face3ds f = mesh.face(i);
				
				// Add the vertex indices to the index vector
				group.indices.put(f.P0);
				group.indices.put(f.P1);
				group.indices.put(f.P2);
				//m.indicesCount += 3;
				
				// Accumulate the normal into the normal vector
				Vector3ds v0 = new Vector3ds(mesh.vertex(f.P0));
				Vector3ds v1 = new Vector3ds(mesh.vertex(f.P1));
				Vector3ds v2 = new Vector3ds(mesh.vertex(f.P2));
				
				Vector3 fn = Vector3.perpendicular(v0, v1, v2);
				fn.normalize();
				fn.x = -fn.x; fn.y = -fn.y; fn.z = -fn.z;
				m.faceNormals.add(fn);
				
			}			
			m.groups.add(group);//With 3ds we only have 1 group at the moment
			
			// Compute the vertices and vertex normals buffers
			m.vertices = FloatBuffer.allocate(mesh.vertices() * 3 * FLOAT_SIZE_BYTES);
			m.texCoords = FloatBuffer.allocate(mesh.texCoords() * 2 * FLOAT_SIZE_BYTES);
			m.vertexNormals = FloatBuffer.allocate(mesh.vertices() * 3 * FLOAT_SIZE_BYTES);
		    Vector3 v = new Vector3();
		    Vector3 n = new Vector3();
			for (int vi=0; vi < mesh.vertices(); vi++)
			{
			      Vertex3ds p = mesh.vertex( vi );
			      TexCoord3ds tc = mesh.texCoord( vi );
			     
			      v.set( p.X, p.Y, p.Z );
			      n.set( 0, 0, 0 );
			      float num = 0;

			      for( int fi=0; fi<mesh.faces(); fi++ )
			      {
			        Face3ds f = mesh.face( fi );
			        Vertex3ds p0 = mesh.vertex(f.P0);
			        Vertex3ds p1 = mesh.vertex(f.P1);
			        Vertex3ds p2 = mesh.vertex(f.P2);

			        if( vi == f.P0 || vi == f.P1 || vi == f.P2 )
			        {
			          num++;
			          n = n.add( m.faceNormals.get(fi));
			        }
			      }

			      if( num > 0 ) n = n.multiply( (float)1.0/(float)num );
			      n.normalize();
			      
			      //Add vertex normal to the model data
			      m.vertexNormals.put(n.x);
			      m.vertexNormals.put(-n.z); //m.vertexNormals.put(n.y);
			      m.vertexNormals.put(n.y); //m.vertexNormals.put(n.z);
			      
			      //Add vertex to the model data
			      m.vertices.put(p.X);
			      m.vertices.put(-p.Z); //m.vertices.put(p.Y);
			      m.vertices.put(p.Y); //m.vertices.put(p.Z);
			      
			      //Add tex coords to the model data
				  m.texCoords.put(tc.U);
				  m.texCoords.put((float) (1.0 - tc.V)); //m.texCoords.put(tc.V);
			      
			     
				  
			      /*if( FLIPYZ )
			      {
			        float tmp = meshes[i].vertexNormals[vi].y;
			        meshes[i].vertexNormals[vi].y = -meshes[i].vertexNormals[vi].z;
			        meshes[i].vertexNormals[vi].z = tmp;
			      }*/
			      
			      // Save vertex data      
			      //if( FLIPYZ )
			      //  meshes[i].vertices[vi] = new Vector3( p.X, -p.Z, p.Y );
			      //else
			      //  mesh.vertices[vi] = new Vector3( p.X, p.Y, p.Z );
			      
			      // Save texcoord data
			      //meshes[i]._numTexCoords = m.texCoords();
			      //if( m.texCoords() > 0 && FLIPV )
			      //  meshes[i].texCoords[vi] = new Vector3( tc.U, 1.0-tc.V, 0 );
			      //else
			      //  meshes[i].texCoords[vi] = new Vector3( tc.U, tc.V, 0 );
				  
			}
			
			/*
			//Compute the vertex normals
			//TODO: Compute the average of all the vertices instead of setting one of the faces normal as a vertex normal
			m.vertexNormals = FloatBuffer.allocate(mesh.vertices() * 3 * FLOAT_SIZE_BYTES);
			for (int i = 0; i < mesh.faces(); i++) 
			{
				Face3ds f = mesh.face(i);
				Vector3 fn = m.faceNormals.get(i);
				
				//PO
				m.vertexNormals.put(f.P0*3, fn.x);
				m.vertexNormals.put(f.P0*3 + 1, fn.y);
				m.vertexNormals.put(f.P0*3 + 2, fn.z);
				
				//P1
				m.vertexNormals.put(f.P1*3, fn.x);
				m.vertexNormals.put(f.P1*3 + 1, fn.y);
				m.vertexNormals.put(f.P1*3 + 2, fn.z);
				
				//P2
				m.vertexNormals.put(f.P2*3, fn.x);
				m.vertexNormals.put(f.P2*3 + 1, fn.y);
				m.vertexNormals.put(f.P2*3 + 2, fn.z);
				
			}
			*/
			
			DebugLog.LOGD("Loader3ds::load3dsModel - new normals computed!");
			DebugLog.LOGD("Loader3ds::load3dsModel - 3DS Model Loading success!");
			DebugLog.LOGD("Loader3ds::load3dsModel - vertices Buffer size: " + m.vertices.array().length);
			DebugLog.LOGD("Loader3ds::load3dsModel - texCoords Buffer size: " + m.texCoords.array().length);
			//DebugLog.LOGD("Loader3ds::load3dsModel - indices Buffer size: " + m.indices.array().length);
			DebugLog.LOGD("Loader3ds::load3dsModel - faceNormals Vector size: " + m.faceNormals.size());
			DebugLog.LOGD("Loader3ds::load3dsModel - vertexNormals Buffer size: " + m.vertexNormals.array().length);
			
			return m;
		} 
		catch (IOException eio) {
		            // handle
			DebugLog.LOGD("Loader3ds::load3dsModel - Failed to create the InputStream");
			return null;
		}
		catch (Exception3ds e3ds) {
			
			DebugLog.LOGD("Loader3ds::load3dsModel - Failed to parse 3ds file: " + e3ds.getMessage());
			return null;
		}
		catch (Exception e) {
			
			DebugLog.LOGD("Loader3ds::load3dsModel - Unknown error when building the model: " + e.getMessage());
			return null;
		}
		
		finally {
			
			 if (stream != null) {
                 try {
                      stream.close();
                 } catch (IOException e) {}
			
			 }
		}
	}

}
